package xyz.scootaloo.mono.security.util

import com.auth0.jwt.JWT
import com.auth0.jwt.algorithms.Algorithm
import com.auth0.jwt.exceptions.JWTDecodeException
import com.auth0.jwt.interfaces.Claim
import com.auth0.jwt.interfaces.DecodedJWT
import xyz.scootaloo.mono.security.token.ClientID
import java.io.UnsupportedEncodingException
import java.util.*

/**
 * Jwt工具
 *
 * 参考 https://www.jianshu.com/p/d1644e281250
 *
 * @author flutterdash@qq.com
 * @since 2021/7/26 15:52
 */
object JwtHelper {

    @Throws(JWTDecodeException::class)
    fun isTokenExpired(token: String): Boolean {
        val now = Calendar.getInstance().time
        return decode(token).expiresAt.before(now)
    }

    /**
     * 给用户签发 jwt
     *
     * @param client 用户客户端标识
     * @param salt   随机盐值, 用于验证用户token有效性
     * @param time   设置指定的过期时间, 单位:秒
     * @return       jwt字符串
     */
    @Throws(UnsupportedEncodingException::class)
    fun sign(
        client: ClientID,
        salt: String,
        time: Long = KW_REDIS.TOKEN_REFRESH_INTERVAL
    ): String {
        val expiredDate = Date(System.currentTimeMillis() + (time * 1000))
        val algorithm = Algorithm.HMAC256(salt)
        return JWT.create().run {
            withClaim(KW.uid, client.uid)
            withClaim(KW.username, client.username)
            withClaim(KW.roleCode, client.roleCode)
            withClaim(KW.appId, client.appId)
            withExpiresAt(expiredDate)
            withIssuedAt(Date())
            sign(algorithm)
        }
    }

    /**
     * 使用[secret]解密[token], 如果解密成功, 则返回jwt中的荷载内容
     *
     * @param token  jwt字符串
     * @param secret 签发此jwt时所用的密匙
     */
    @Throws(JWTDecodeException::class)
    fun verifyToken(token: String, secret: String): Map<String, Claim> {
        val verifier = JWT.require(Algorithm.HMAC256(secret)).build()
        val jwt = verifier.verify(token)
        return jwt.claims
    }

    private fun decode(token: String): DecodedJWT = JWT.decode(token)

    class TokenHolder(
        val token: String,
        var username: String = "",
        var appId: String = ""
    ) {
        private val jwt = decode(token)
        init {
            username = jwt.getClaim(KW.username).asString()
            appId = jwt.getClaim(KW.appId).asString()
        }
    }
}
